package IC.lir;

import java.util.Arrays;
import java.util.LinkedList;
import java.lang.Math;

import IC.AST.*;
import IC.SymbolTables.Types.Symbol;
import IC.SymbolTables.Types.BasicType.BasicTypeEnum;

/**
 * 
 * @author Me :P
 * 
 * assuming the ImmediatesCalusVisitor was used on the tree previously.
 *
 */
public class RegisterWeightCalcVisitor extends Visitor 
{

	private boolean _returnAsMemoryFlag = false,
			_returnToDummyRegFlag = false;
	
	public Object Visit(Program rootNode)
	{
		_returnAsMemoryFlag = false;
		_returnToDummyRegFlag = false;
		return VisitSons(rootNode); 
	}
	
	public Object Visit(ClassDeclaration node) { return VisitSons(node); }
	public Object Visit(Method node) { return VisitSons(node); }
	public Object Visit(ScopeStatement node) { return VisitSons(node); }
	
	public Object Visit(DeclorationStatement node) { return VisitSons(node); }
	public Object Visit(ContinueStatement node) { return VisitSons(node); }
	public Object Visit(BreakStatement node) { return VisitSons(node); }
	public Object Visit(WhileStatement node) { return VisitSons(node); }
	public Object Visit(IfStatement node) { return VisitSons(node); }
	public Object Visit(AssignStatement node)
	{
		_returnAsMemoryFlag = true;
		node.GetWhereTo().Visit(this);
		_returnAsMemoryFlag = false;
		
		node.GetWhat().Visit(this);
		InsureNotNegativeImmediate(node.GetWhat());
		
		return null;
	}
	
	public Object Visit(ReturnStatement node)
	{
		if (node.GetReturnValue() == null)
			return null;
		
		_returnAsMemoryFlag = true;
		node.GetReturnValue().Visit(this);
		_returnAsMemoryFlag = false;
		return null; 
	}
	
	public Object Visit(FunctionCallStatement node)
	{ 
		_returnToDummyRegFlag = true;
		node.GetFunctionCall().Visit(this);
		_returnToDummyRegFlag = false;
		return null;
	}
	
	
	public Object Visit(BinaryOperationExpression node)
	{
		if (node.IsExpValueSet())
		{
			node.SetNumOfRegisters(0);
			return null;
		}
		
		boolean returnAsMemoryFlag = _returnAsMemoryFlag;
		
		if (node.GetType().equals(new IC.SymbolTables.Types.BasicType(BasicTypeEnum.STRING)))
			CalcBinarySonsWithMemFlags(node, true, true);
		else if (node.GetType().equals(new IC.SymbolTables.Types.BasicType(BasicTypeEnum.INT)) && !node.GetOperation().Isflippable())
		{
			CalcBinarySonsWithMemFlags(node, false, true);
			if (node.GetFirstExp().IsExpValueSet())
				node.GetFirstExp().SetNumOfRegisters(1);
		}
		else	//All flippable operators (including all boolean operators like ==, !=, <, ...
		{
			CalcBinarySonsWithMemFlags(node, false, true);
			int lastRegNumForNode = node.GetNumOfRegisters();
			
			//try to evaluate in opposite order (maybe the first can be returned as memory) 
			CalcBinarySonsWithMemFlags(node, true, false);
			
			if (lastRegNumForNode < node.GetNumOfRegisters())
				CalcBinarySonsWithMemFlags(node, false, true);
		}
		
		_returnAsMemoryFlag = returnAsMemoryFlag;
		return null; 
	}
	
	private void CalcBinarySonsWithMemFlags(BinaryOperationExpression node, boolean firstFlag, boolean secondFlag)
	{
		boolean lastReturnAsMemFlag = _returnAsMemoryFlag;
		
		node.GetFirstExp().SetNumOfRegisters(-1);
		_returnAsMemoryFlag = firstFlag;
		node.GetFirstExp().Visit(this);

		node.GetSecondExp().SetNumOfRegisters(-1);
		_returnAsMemoryFlag = secondFlag;
		node.GetSecondExp().Visit(this);
		
		_returnAsMemoryFlag = lastReturnAsMemFlag;
		
		InsureNotNegativeImmediate(node.GetFirstExp());
		InsureNotNegativeImmediate(node.GetSecondExp());
		
		node.SetNumOfRegisters(node.GetFirstExp().GetNumOfRegisters() == node.GetSecondExp().GetNumOfRegisters() ?
				node.GetFirstExp().GetNumOfRegisters() + 1
			:
				Math.max(node.GetFirstExp().GetNumOfRegisters(), 
						node.GetSecondExp().GetNumOfRegisters()));
	}
	
	public Object Visit(UnaryOperationExpression node) 
	{
		if (node.IsExpValueSet())
		{
			node.SetNumOfRegisters(0);
			return null;
		}
		
		node.GetExpression().Visit(this);
		node.SetNumOfRegisters(Math.max(node.GetExpression().GetNumOfRegisters(), 1));
		return null;
	}

	public Object Visit(NewArrayExpression node)
	{
		node.GetSizeExpression().Visit(this);
		node.SetNumOfRegisters(Math.max(
				node.GetSizeExpression().GetNumOfRegisters(),
				1));
		node.SetNumOfRegisters(node.GetSizeExpression().GetNumOfRegisters());
		return null;
	}
	
	public Object Visit(NewClassExpression node)
	{
		node.SetNumOfRegisters(1);
		return null;
	}
	
	public Object Visit(LiteralExpression node)
	{
		node.GetLiteral().Visit(this);
		node.SetNumOfRegisters(node.GetLiteral().GetNumOfRegisters());
		return null; 
	}
	public Object Visit(LengthExpression node)
	{
		boolean returnAsMemoryFlag = _returnAsMemoryFlag;
		
		_returnAsMemoryFlag = true;
		node.GetExpression().Visit(this);
		_returnAsMemoryFlag = returnAsMemoryFlag;
		
		node.SetNumOfRegisters(
				node.GetExpression().GetNumOfRegisters() == 0 ?
						1
					:
						node.GetExpression().GetNumOfRegisters());
		return null; 
	}
	
	//the function is 100% not of void return type because :
	//		an expression must return it value somewhere
	public Object Visit(FunctionCallExpression node)
	{
		 node.GetFunctionCall().Visit(this);
		 node.SetNumOfRegisters(node.GetFunctionCall().GetNumOfRegisters());
		return null;
	}
	
	//using for return register the same as for the object 
	public Object Visit(VirtualFunctionCall node)
	{

		boolean retAsMem = _returnAsMemoryFlag;
		
		_returnAsMemoryFlag = false;
		node.GetInvokedExpression().Visit(this);
		_returnAsMemoryFlag = retAsMem;
		
		node.GetParams().addFirst(node.GetInvokedExpression());
		node.SetNumOfRegisters(GetMaxRegisterForParams(node.GetParams(), true));
		node.GetParams().removeFirst();
		
		return null;
	}
	
	//Reusing one of the registers from parameters for return if such exist, 
	//	other wise needed one register for return if not _returnToDummyRegFlag  
	public Object Visit(StaticFunctionCall node)
	{
		node.SetNumOfRegisters(Math.max(
				GetMaxRegisterForParams(node.GetParams(), false),
				_returnToDummyRegFlag ? 0 : 1));
		return null;
	}
	
	private int GetMaxRegisterForParams(LinkedList<Expression> params, boolean isVirtualCall)
	{
		boolean retAsMem = _returnAsMemoryFlag;
		_returnAsMemoryFlag = true;
		
		boolean isFirst = false;
		for (Expression param : params)
		{
			if (isFirst && isVirtualCall)
			{
				_returnAsMemoryFlag = false;
				param.Visit(this);
				_returnAsMemoryFlag = true;
				isFirst = false;
				continue;
			}
			
			param.Visit(this);
			InsureNotNegativeImmediate(param);
		}
		
		Expression[] paramsArr = new Expression[params.size()];
		int index = 0;
		for (Expression param : params)
			paramsArr[index++] = param;
			
			
		Arrays.sort(paramsArr,  new java.util.Comparator<Expression>()  
				{
					/**
					 * sorting from the biggest to the smallest?
					 */
					public int compare(Expression o1, Expression o2) 
					{
						return o2.GetNumOfRegisters() - o1.GetNumOfRegisters(); 
					}
				});
		
		int ret = 0;
		index = 0;
		for (Expression param : paramsArr)
			ret = (param.GetNumOfRegisters()+ index++ == ret) ? 
						ret + 1
					:
						Math.max(ret, param.GetNumOfRegisters() + index);

		_returnAsMemoryFlag = retAsMem;
		return ret;
	}

	public Object Visit(ThisExpression node)
	{ 
		node.SetNumOfRegisters(_returnAsMemoryFlag ? 0 : 1);
		return null; 
	}
	
	public Object Visit(BooleanLiteral node)
	{
		node.SetNumOfRegisters(0);
		return null;
	}
	
	public Object Visit(NullLiteral node)
	{
		node.SetNumOfRegisters(0);
		return null;
	}
	
	public Object Visit(IntegerLiteral node){
		node.SetNumOfRegisters(0);
		return null;
	}
	
	public Object Visit(QuoteLiteral node)
	{
		node.SetNumOfRegisters(_returnAsMemoryFlag ? 0 : 1);
		return null;
	}
	
	public Object Visit(SimpleLocation node)
	{
		Symbol tempSym;
		if ((tempSym = node.GetSymbolTable().GetSymbol(node.GetID(), false)) == null)
			tempSym = node.GetSymbolTable().GetSymbol(node.GetID(), true);
		
		//if field one register needed for "this" and the same one used for return
		node.SetNumOfRegisters(
				_returnAsMemoryFlag && !tempSym.GetKind().equals(Symbol.Kind.FIELD) ?
						0 
					: 	
						1);
		return null;
	}
	
	//using the same register for return as the one for the object
	public Object Visit(DotLocation node)
	{
		boolean returnAsMemoryFlag = _returnAsMemoryFlag;
		
		_returnAsMemoryFlag = false;
		node.GetLeftExpression().Visit(this);
		_returnAsMemoryFlag = returnAsMemoryFlag;
		
		node.SetNumOfRegisters(node.GetLeftExpression().GetNumOfRegisters());
		return null;
	}
	
	//using the same register for return as the one for the object
	public Object Visit(ArrayLocation node)
	{
		boolean returnAsMemoryFlag = _returnAsMemoryFlag;
		
		_returnAsMemoryFlag = false;
		node.GetArray().Visit(this);

		_returnAsMemoryFlag = true;
		node.GetIndex().Visit(this);
		InsureNotNegativeImmediate(node);

		_returnAsMemoryFlag = returnAsMemoryFlag;
		
		node.SetNumOfRegisters(Math.max(
				node.GetArray().GetNumOfRegisters(),
				node.GetIndex().GetNumOfRegisters()));
		
		if (returnAsMemoryFlag && node.GetArray().GetNumOfRegisters() == node.GetIndex().GetNumOfRegisters())
			node.SetNumOfRegisters(node.GetIndex().GetNumOfRegisters() + 1);
		
		return null;
	}
		
	private void InsureNotNegativeImmediate(ASTNode node)
	{
		if (node.IsExpValueSet() && node.GetExpValue() < 0)
			node.SetNumOfRegisters(node.GetNumOfRegisters() + 1);
	}
}
